#!/usr/bin/env python
'''Simple launcher'''

__author__ = "Miki Tebeka <miki.tebeka@gmail.com>"
__license__ = "BSD" # see LICENSE
__version__ = "0.2.1"

from Tkinter import Tk, Label, Entry, BOTH, Listbox, END
from tkFont import Font
from tkMessageBox import showerror
from threading import Timer

from os.path import exists, expanduser, join, isfile
from os import environ, popen, stat, system
from stat import S_IEXEC
from string import strip

HISTORY = []
HISTORY_INDEX = 0

def config_file(base_file, env_key):
    filename = ""
    if env_key in environ:
        return environ[env_key]
    elif "HOME" in environ:
        return join(environ["HOME"], base_file)

    return ""


def load_aliases():
    rcfile = config_file(".tllrc", "TLLRC")

    if (not rcfile) or (not isfile(rcfile)):
        return {}

    aliases = {}
    for line in open(rcfile):
        line = line.strip()
        if (not line) or (line[0] == "#"):
            continue
        alias, target = map(strip, line.split("=", 1))

        aliases[alias] = target

    return aliases

def unique(items):
    seen = set()
    for item in items:
        if item in seen:
            continue
        seen.add(item)
        yield item

histnames = (".tll_history", "TLL_HISTORY")
def load_history():
    global HISTORY

    histfile = config_file(*histnames)

    if (not histfile) or (not isfile(histfile)):
        return []

    HISTORY = filter(None, map(strip, open(histfile)))
    history = list(unique(HISTORY))

def save_history():
    histfile = config_file(*histnames)
    if not histfile:
        return

    fo = open(histfile, "wt")
    for item in unique(HISTORY):
        print >> fo, item
    fo.close()

def update_history(command, args):
    newline = ("%s %s" % (command, args)).strip()
    for lnum, line in enumerate(HISTORY):
        if line == newline:
            del HISTORY[lnum]
    HISTORY.insert(0, newline)

def is_executable(path):
    if not isfile(path):
        return 0

    return S_IEXEC & stat(path).st_mode

def launch(name, args, aliases):
    fullname = aliases.get(name, name)

    if " " in fullname:
        fullname, xargs = fullname.split(" ", 1)
        args = "%s %s" % (xargs, args)

    fullname = expanduser(fullname)
    if not exists(fullname):
        fullname = popen("which %s 2>/dev/null" % fullname).read().strip()
        if not fullname:
            raise ValueError("can't find %s" % name)

    if is_executable(fullname):
        system("\"%s\" %s&" % (fullname, args))
    else:
        system("start \"%s\"" % fullname)


USER_CANCEL = 0
ROOT = None
COMMAND = None

def quit(event):
    global USER_CANCEL

    USER_CANCEL = 1
    ROOT.quit()

def move(step):
    global HISTORY_INDEX

    if not HISTORY:
        return

    HISTORY_INDEX = (HISTORY_INDEX + step) % len(HISTORY)
    COMMAND.delete(0, END)
    COMMAND.insert(0, HISTORY[HISTORY_INDEX])

def build_ui():
    global ROOT, COMMAND

    ROOT = Tk()
    ROOT.title("TLL")
    ROOT.bind("<Escape>", quit)
    font = Font(family="Courier", size=40, weight="bold")
    COMMAND = Entry(width=20, font=font)
    COMMAND.pack(fill=BOTH)
    COMMAND.bind("<Return>", lambda e: ROOT.quit())
    COMMAND.bind("<Up>", lambda e: move(-1))
    COMMAND.bind("<Down>", lambda e: move(1))

    if HISTORY:
        COMMAND.insert(0, HISTORY[0])
        COMMAND.selection_range(0, END)

def show_ui():
    global USER_CANCEL

    USER_CANCEL = 0
    COMMAND.focus()

    timer = Timer(30, quit, (None, ))
    timer.start()
    ROOT.mainloop()
    timer.cancel()

    return COMMAND.get().strip()

def set_user_path():
    if "HOME" not in environ:
        return

    new_path = "%s:%s" % (join(environ["HOME"], "bin"), environ["PATH"])
    environ["PATH"] = new_path

def main(argv=None):
    if argv is None:
        import sys
        argv = sys.argv

    from optparse import OptionParser

    parser = OptionParser(usage="usage: %prog", 
            version="tll %s" % __version__)

    opts, args = parser.parse_args(argv[1:])
    if len(args) != 0:
        parser.error("wrong number of arguments") # Will exit


    aliases = load_aliases()
    load_history()
    set_user_path()

    build_ui()
    while 1:
        try:
            command = show_ui()
            if USER_CANCEL:
                raise SystemExit

            if not command:
                showerror("TLL Error", "Please enter *something*")
                continue

            if " " in command:
                command, args = command.split(" ", 1)
            else:
                args = ""
            launch(command, args, aliases)
            update_history(command, args)
            save_history()
            break
        except ValueError:
            showerror("TLL Error", "Can't launch %s" % command)

if __name__ == "__main__":
    main()

